package com.mobbanana.gamehook.utils;

import android.content.Context;
import android.util.Log;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;

import dalvik.system.DexClassLoader;

/**
 * @Author: imchen
 * @Create: 2020/10/29 11:22
 */
public class JDexUtil {
    public static void LoadDex(Context ctx,String filePath){
        File file = new File(filePath);
        if(!file.exists()){
            Log.i("Test", "Dex file not exists, file: " + file.getAbsolutePath());
            return;
        }
        //含有dex的jar或apk文件的路径
        String dexPath = file.getAbsolutePath();

        File dir = new File(ctx.getCacheDir(), "optDir");
        if(!dir.exists()){
            dir.mkdir();
        }
        //优化后的dex文件存放的目录的路径
        String optimizedDirectory = dir.getAbsolutePath();

        try {
            //应用的加载器
            ClassLoader pathClassLoader = ctx.getClassLoader();
            //动态加载器，加载的dex文件
            //因为没有从外部引入so文件，所以第3个参数为null
            DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedDirectory, null, pathClassLoader);

            //1, 获取DexClassLoader的pathList
            Field dexPathListField = JReflectUtil.getField(dexClassLoader.getClass(), "pathList");
            Object dexPathList = dexPathListField.get(dexClassLoader);
            if(dexPathList == null){
                Log.i("Test", "Fail to get pathList from DexClassLoader, dexClassLoader: " + dexClassLoader);
                return;
            }

            //2, 获取pathList的dexElements
            Field dexDexElementsField = JReflectUtil.getField(dexPathList.getClass(), "dexElements");
            Object[] dexDexElements = (Object[]) dexDexElementsField.get(dexPathList);
            if(dexDexElements == null){
                Log.i("Test", "Fail to get dexElements from pathList in DexClassLoader, dexClassLoader: " + dexClassLoader);
                return;
            }

            if(dexDexElements.length == 0){
                Log.i("Test", "The size of dexElements from pathList in DexClassLoader is 0, dexClassLoader: " + dexClassLoader);
                return;
            }

            //3, 获取应用加载器PathClassLoader的pathList
            Field pathPathListField = JReflectUtil.getField(pathClassLoader.getClass(), "pathList");
            Object pathPathList = pathPathListField.get(pathClassLoader);
            if(pathPathList == null){
                Log.i("Test", "Fail to get pathList from application ClassLoader, classLoader: " + pathClassLoader);
                return;
            }

            //4, 获取应用加载器的pathList的dexElements
            Field pathDexElementsField = JReflectUtil.getField(pathPathList.getClass(), "dexElements");
            Object[] pathDexElements = (Object[]) pathDexElementsField.get(pathPathList);
            if(pathDexElements == null){
                Log.i("Test", "Fail to get dexElements from pathList in application ClassLoader, classLoader: " + pathClassLoader);
                return;
            }

            //5, 创建新数组，并复制Element到新数组
            //创建长度为dexDexElements.length + pathDexElements.length的Element[]
            Object[] newDexElements = (Object[]) Array.newInstance(pathDexElements.getClass().getComponentType(), dexDexElements.length + pathDexElements.length);
            //将动态加载的dexElements复制到newDexElements，范围：[0，dexDexElements.length-1]
            System.arraycopy(dexDexElements, 0, newDexElements, 0, dexDexElements.length);
            //将应用的dexElements复制到newDexElements，范围：[dexDexElements.length, n]
            System.arraycopy(pathDexElements, 0, newDexElements, dexDexElements.length, pathDexElements.length);

            //6，将newDexElements设置给应用加载器的pathList的dexElements
            pathDexElementsField.set(pathClassLoader, newDexElements);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
